﻿#region 版权说明
/**************************************************************************
 * 文 件 名：DbHelper
 * 命名空间：CodeGenerator.Core
 * 描　　述：
 * 版 本 号：V1.0.0
 * 作　　者：long
 * 创建时间：2019/5/16 18:02:13
 * CLR 版本：4.0.30319.42000
 * 机器名称：DESKTOP-PHQQ0O3
***************************************************************************	
 * 修 改 人：
 * 时    间：
 * 修改说明：
***************************************************************************
 * Copyright  2018 河南广慧会计服务有限公司 Inc. All Rights Reserved
***************************************************************************/
#endregion
using CodeGenerator.ViewModel.DbViewModel;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeGenerator.Core
{
    public class DbHelper
    {
        #region GetAllData
        public static List<DbTable> GetAllData(string connectionString, string database)
        {
            var result = GetDbTables(connectionString, database);
            var foreignKeys = GetDbForeignKeyTable(connectionString, database);
            result.ForEach(e => {
                e.dbColumns = GetDbColumns(connectionString, database, e.TableName.ToLower());
                e.AllForeignKeys = foreignKeys;
            });
            return result;
        }
        #endregion

        #region GetDbTables



        public static List<DbTable> GetDbTables(string connectionString, string database, string tables=null)
        {
            if (!string.IsNullOrEmpty(tables))
            {
                tables = string.Format(" and obj.name in ('{0}')", tables.Replace(",", "','"));
            }
            #region SQL
            string sql = string.Format(@"SELECT
                                    obj.name tablename,
                                    schem.name schemname,
                                    idx.rows,
                                    CAST
                                    (
                                        CASE 
                                            WHEN (SELECT COUNT(1) FROM sys.indexes WHERE object_id= obj.OBJECT_ID AND is_primary_key=1) >=1 THEN 1
                                            ELSE 0
                                        END 
                                    AS BIT) HasPrimaryKey                                         
                                    from {0}.sys.objects obj 
                                    inner join {0}.dbo.sysindexes idx on obj.object_id=idx.id and idx.indid<=1
                                    INNER JOIN {0}.sys.schemas schem ON obj.schema_id=schem.schema_id
                                    where type='U' {1}
                                    order by obj.name", database, tables);
            #endregion
            DataTable dt = GetDataTable(connectionString, sql);
            return dt.Rows.Cast<DataRow>().Select(row => new DbTable
            {
                TableName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(row.Field<string>("tablename").ToLower()),
                SchemaName = row.Field<string>("schemname").ToLower(),
                Rows = row.Field<int>("rows"),
                HasPrimaryKey = row.Field<bool>("HasPrimaryKey")
            }).ToList();
        }
        #endregion

        #region GetDbColumns

        public static List<DbColumn> GetDbColumns(string connectionString, string database, string tableName, string schema="dbo")
        {
            #region SQL
            string sql = string.Format(@"
                                    WITH indexCTE AS
                                    (
                                        SELECT 
                                        ic.column_id,
                                        ic.index_column_id,
                                        ic.object_id    
                                        FROM {0}.sys.indexes idx
                                        INNER JOIN {0}.sys.index_columns ic ON idx.index_id = ic.index_id AND idx.object_id = ic.object_id
                                        WHERE  idx.object_id =OBJECT_ID(@tableName) AND idx.is_primary_key=1
                                    )
                                    select
                                    colm.column_id ColumnID,
                                    CAST(CASE WHEN indexCTE.column_id IS NULL THEN 0 ELSE 1 END AS BIT) IsPrimaryKey,
                                    colm.name ColumnName,
                                    systype.name ColumnType,
                                    colm.is_identity IsIdentity,
                                    colm.is_nullable IsNullable,
                                    cast(colm.max_length as int) ByteLength,
                                    (
                                        case 
                                            when systype.name='nvarchar' and colm.max_length>0 then colm.max_length/2 
                                            when systype.name='nchar' and colm.max_length>0 then colm.max_length/2
                                            when systype.name='ntext' and colm.max_length>0 then colm.max_length/2 
                                            else colm.max_length
                                        end
                                    ) CharLength,
                                    cast(colm.precision as int) Precision,
                                    cast(colm.scale as int) Scale,
                                    prop.value Remark
                                    from {0}.sys.columns colm
                                    inner join {0}.sys.types systype on colm.system_type_id=systype.system_type_id and colm.user_type_id=systype.user_type_id
                                    left join {0}.sys.extended_properties prop on colm.object_id=prop.major_id and colm.column_id=prop.minor_id
                                    LEFT JOIN indexCTE ON colm.column_id=indexCTE.column_id AND colm.object_id=indexCTE.object_id                                        
                                    where colm.object_id=OBJECT_ID(@tableName)
                                    order by colm.column_id", database);
            #endregion
            SqlParameter param = new SqlParameter("@tableName", SqlDbType.NVarChar, 100) { Value = string.Format("{0}.{1}.{2}", database, schema, tableName) };
            DataTable dt = GetDataTable(connectionString, sql, param);
            return dt.Rows.Cast<DataRow>().Select(row => new DbColumn()
            {
                ColumnID = row.Field<int>("ColumnID"),
                IsPrimaryKey = row.Field<bool>("IsPrimaryKey"),
                ColumnName = row.Field<string>("ColumnName"),
                ColumnType = row.Field<string>("ColumnType"),
                IsIdentity = row.Field<bool>("IsIdentity"),
                IsNullable = row.Field<bool>("IsNullable"),
                ByteLength = row.Field<int>("ByteLength"),
                CharLength = row.Field<int>("CharLength"),
                Scale = row.Field<int>("Scale"),
                Remark = row["Remark"].ToString()
            }).ToList();
        }

        #endregion


        #region GetDbForeignKeyTable

        public static List<ForeignKeyViewModel> GetDbForeignKeyTable(string connectionString, string database)
        {

            //and object_name(b.rkeyid)='order'
            #region SQL
            string sql = string.Format(@"
                                        select *,ForeignPrimaryKeyName = 
                                        (SELECT c.name Cname FROM [{0}].sys.objects T INNER JOIN [{0}].sys.objects P 
									        ON t.object_id=p.parent_object_id AND t.type='U' AND p.type='PK'
									        INNER JOIN [{0}].sys.SysColumns C ON c.id=t.object_id 
									        INNER JOIN sysindexes i ON i.name=p.name
									        INNER JOIN sysindexkeys k ON k.id=c.id AND k.colid=c.colid AND k.indid=i.indid and t.name= a.ForeignKeyTableName)
							         from (
									        SELECT PrimaryKeyTableName=object_name(b.rkeyid) 
                                                    ,PrimaryKeyName=(SELECT name FROM syscolumns WHERE colid=b.rkey AND id=b.rkeyid) 
                                                    ,ForeignKeyTableName=object_name(b.fkeyid) 
                                                    ,ForeignKeyName=(SELECT name FROM syscolumns WHERE colid=b.fkey AND id=b.fkeyid)  
                                                FROM [{0}].sys.sysobjects a 
                                                    join [{0}].sys.sysforeignkeys b on a.id=b.constid 
                                                    join [{0}].sys.sysobjects c on a.parent_obj=c.id 
                                                where a.xtype='f' AND c.xtype='U' ) as a
                                   ", database);
            #endregion
            //SqlParameter param = new SqlParameter("@tableName", SqlDbType.NVarChar, 100) { Value = string.Format("{0}.{1}.{2}", database, schema, tableName) };
            DataTable dt = GetDataTable(connectionString, sql);
            return dt.Rows.Cast<DataRow>().Select(row => new ForeignKeyViewModel()
            {
                PrimaryKeyTableName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(row.Field<string>("PrimaryKeyTableName").ToLower()),
                PrimaryKeyName = row.Field<string>("PrimaryKeyName"),
                ForeignKeyTableName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(row.Field<string>("ForeignKeyTableName").ToLower()),
                ForeignKeyName = row.Field<string>("ForeignKeyName"),
                ForeignPrimaryKeyName = row.Field<string>("ForeignPrimaryKeyName")
            }).ToList();
        }

        #endregion

        #region GetDataTable

        public static DataTable GetDataTable(string connectionString, string commandText, params SqlParameter[] parms)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand command = connection.CreateCommand();
                command.CommandText = commandText;
                command.Parameters.AddRange(parms);
                SqlDataAdapter adapter = new SqlDataAdapter(command);

                DataTable dt = new DataTable();
                adapter.Fill(dt);

                return dt;
            }
        }

        #endregion
    }

}
